package org.netty.framework.tcpserver.codec;

import java.lang.reflect.Method;
import java.util.List;

import org.netty.framework.core.codec.CustomMsg;
import org.netty.framework.core.codec.CustomMsgDecoder;
import org.netty.framework.core.codec.Header;
import org.netty.framework.core.util.AnnotationContainer;
import org.netty.framework.tcpserver.util.PacketUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.protobuf.GeneratedMessageV3;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageCodec;

/**
 * 这里解释一下为什么不使用注解？
 * 
 * 当发生断线重连的时候，TcpServerCodec相当于多次添加到ChannelPipelines中，这是必须添加@ChannelHandler.Sharable注解，
 * 然而每一个CommandCodecHandler都有针对某个socket的累积对象,CommandCodecHandler是一个不可以共享的对象类型，贴出部分源码：
 * static void ensureNotSharable(ChannelHandlerAdapter handler) { if
 * (handler.isSharable()) { throw new IllegalStateException("@Sharable
 * annotation is not allowed"); } } 通过源码发现 ByteToMessageCodec
 * 不允许使用@ChannelHandler.Sharable，所以这里需要每次都新建一个CommandCodecHandler
 */
public class TcpServerCodec extends ByteToMessageCodec<CustomMsg<?>> {

	private static final Logger logger = LoggerFactory.getLogger(TcpServerCodec.class);

	private Class<?> clazz;

	public TcpServerCodec(Class<?> clazz) {
		this.clazz = clazz;
	}

	/**
	 * CustomMsg消息编码
	 */
	@Override
	protected void encode(ChannelHandlerContext ctx, CustomMsg<?> msg, ByteBuf frame) throws Exception {
		try {
			int headerLength = PacketUtil.buildPackageHeader(frame, msg);
			GeneratedMessageV3 bodyMsg = (GeneratedMessageV3) msg.getBody();
			byte[] body = bodyMsg.toByteArray();
			frame.writeBytes(body);
			
			int totalLength = headerLength + body.length - 4;
			// 此处设置此帧数据包的长度(占4个字节)
			frame.setInt(0, totalLength);
		} catch (Exception e) {
			logger.error("响应客户端失败:{}", e);
		}
	}

	/**
	 * CustomMsg消息解码,这里会根据消息头中的cmd解码为对应CustomMsg的子类
	 */
	@Override
	protected void decode(ChannelHandlerContext ctx, ByteBuf frame, List<Object> out) throws Exception {
		if (frame == null || frame.readableBytes() == 0) {
			return;
		}
		Header header = PacketUtil.parsePacketHeader(frame, clazz);
		if (header != null) {
			if (logger.isDebugEnabled()) {
				Method toString = clazz.getMethod("toString");
				logger.debug("消息头:{}", toString.invoke(header));
			}
			/**
			 * 读取消息体
			 */
			byte[] bodyData = PacketUtil.readPacketBody(frame, header);
			long cmd = header.getCmd();
			CustomMsgDecoder<CustomMsg<?>> decoder = AnnotationContainer.getDecoder(cmd);
			if (decoder == null) {
				logger.warn("没有找到对应的解码器,cmd:{}", cmd);
			} else {
				// 解码命令
				CustomMsg<?> customMsg = decoder.decode(header, bodyData);
				out.add(customMsg);
			}
		}
	}

}
